﻿using Scriban;
using XinLife.Application.Interface;
using XinLife.Model.Dto;
using System.Data;
using System.Text;
using System.Text.RegularExpressions;
using XinLife.Core.Model;
using XinLife.Core.Extention;

namespace XinLife.Application.Implement
{
    public class CodeService : BaseService, ICodeService
    {
        IModuleService _moduleService;
        public CodeService(IBaseRepository baseRepository, IModuleService moduleService) : base(baseRepository)
        {
            _moduleService = moduleService;
        }
        public async Task<dynamic> CodeGenerate(CodeGenerateInputDto dto)
        {
            string[] IgnoreCols = { "cId", "cCreatedBy", "cCreatedDate", "cUpdatedBy", "cUpdatedDate" };
            string path = AppContext.BaseDirectory;

            var columnList = await GetSchema(dto.TableName);
            //去除前面tb_ 例如 tb_log_info 转为 LogInfo
            string model_name = Regex.Replace(dto.TableName, "_(.)",match => match.Groups[1].Value.ToUpper()).TrimStart('t','b');
            //小写字母开头  logInfo
            string l_model_name = model_name.Substring(0, 1).ToLower() + model_name.Substring(1);
            //html用的  log-info
            string web_table_name = dto.TableName.ToLower().Replace("tb_", "").Replace("_","-");
            //控制器
            string content = File.ReadAllText(Path.Combine(path, "Templates", "Controller.sbntxt"));
            var template = Template.Parse(content);
            string controllerText = template.Render(new { model_name, l_model_name, description = dto.setInfo.description });
            //接口
            content = File.ReadAllText(Path.Combine(path, "Templates", "IService.sbntxt"));
            template = Template.Parse(content);
            string interfaceText = template.Render(new { model_name });
            //实现
            content = File.ReadAllText(Path.Combine(path, "Templates", "Service.sbntxt"));
            template = Template.Parse(content);
            string implementText = template.Render(new { model_name, searchfields = dto.searchFields.OrderBy(t => t.sortCode).ToList() });
            //实体
            content = File.ReadAllText(Path.Combine(path, "Templates", "Model.sbntxt"));
            template = Template.Parse(content);
            string modelText = template.Render(new { model_name, table_name = dto.TableName.ToLower(), description = dto.setInfo.description, columns = columnList.Where(t => !IgnoreCols.Contains(t.columnname)).ToList() });
            //web页面 
            content = File.ReadAllText(Path.Combine(path, "Templates", "Web.html"));
            template = Template.Parse(content);
            string webText = template.Render(new { model_name, web_table_name, l_model_name, description = dto.setInfo.description, columns = columnList.Where(t => !IgnoreCols.Contains(t.columnname)).ToList(), searchfields = dto.searchFields.OrderBy(t => t.sortCode).ToList() });
            //webform页面
            content = File.ReadAllText(Path.Combine(path, "Templates", "WebForm.html"));
            template = Template.Parse(content);
            string formText = template.Render(new { model_name, formfield = dto.formFields });

            return new { modelText, controllerText, interfaceText, implementText, webText, formText };
        }

        public async Task<PageData<object>> GetTablePageList(Pagination page, string queryJson = null)
        {
            var queryParam = queryJson.ToJObject();
            StringBuilder strSql = new StringBuilder();
            strSql.Append(@"SELECT t1.*,
                                (select COLUMN_NAME from information_schema.`COLUMNS` where TABLE_SCHEMA=database() and  TABLE_NAME =t1.name and COLUMN_KEY='PRI') pk
                                 from (
                                SELECT TABLE_NAME name,0 reserved,0 data,0 index_size,TABLE_ROWS sumrows,0 unused, (select IF(LENGTH(TRIM(TABLE_COMMENT))<1,TABLE_NAME,TABLE_COMMENT)) tdescription
                                from information_schema.`TABLES` where TABLE_SCHEMA =database()
                                ) t1 
                            WHERE 1 = 1 ");
            if (!queryParam["tableName"].IsEmpty())
            {
                strSql.Append(" AND t1.name='" + queryParam["tableName"].ToString() + "'");
            }
            strSql.Append(" ORDER BY t1.name");

            var data = await _db.Ado.SqlQueryAsync<object>(strSql.ToString());
            return new PageData<object>() { entitys = data, count = data.Count };

        }
        public async Task<AjaxResult> GetTableFiled(string tableName)
        {
            StringBuilder strSql = new StringBuilder();
            strSql.Append(@"select  ORDINAL_POSITION number, COLUMN_NAME `column`, DATA_TYPE datatype, if(CHARACTER_MAXIMUM_LENGTH is null,if(LOCATE('int',column_type)>0,REPLACE(REPLACE(column_type,'int(',''),')',''),0),CHARACTER_MAXIMUM_LENGTH) length, '' identity, IF(COLUMN_KEY='PRI','1','') 	`key`,
                                        IF(IS_NULLABLE='YES','1','') isnullable,COLUMN_DEFAULT `default`,case when COLUMN_COMMENT='' then COLUMN_NAME else COLUMN_COMMENT END remark from (
                                        select  *
                                         from information_schema.`COLUMNS` t1 where TABLE_SCHEMA=database() and TABLE_NAME = ?tableName
                                        ) t2 order by number");
            var data = await _db.Ado.SqlQueryAsync<object>(strSql.ToString(), new { tableName = tableName });
            return Success(data);
        }


        public async Task<List<SchemaInfo>> GetSchema(string tableName)
        {
            StringBuilder sbSQL = new StringBuilder();
            sbSQL.AppendLine("SELECT ");
            sbSQL.AppendLine("ORDINAL_POSITION  f_number,");
            sbSQL.AppendLine("COLUMN_NAME,");
            sbSQL.AppendLine("DATA_TYPE,");
            sbSQL.AppendLine("COLUMN_COMMENT"); ;
            sbSQL.AppendLine("FROM INFORMATION_SCHEMA.COLUMNS  ");
            sbSQL.AppendFormat("WHERE TABLE_SCHEMA = database() and TABLE_NAME='{0}'", tableName);

            var db = await _db.Ado.GetDataTableAsync(sbSQL.ToString(), new { tableName });

            List<SchemaInfo> list = new List<SchemaInfo>();
            foreach (DataRow dr in db.Rows)
            {
                SchemaInfo info = new SchemaInfo();
                info.columndesc = dr["COLUMN_COMMENT"].ToString();
                info.columnname = dr["COLUMN_NAME"].ToString();
                info.columntype = TypeConvertor.MapType(dr["DATA_TYPE"].ToString());
                list.Add(info);
            }
            return list;
        }

        public async Task<AjaxResult> SaveMenu(CodeGenerateInputDto dto)
        {
            string table_name = Regex.Replace(dto.TableName, "_(.)", match => match.Groups[1].Value.ToUpper()).TrimStart('t', 'b');

            var code = await CodeGenerate(dto);

            string path = Directory.GetCurrentDirectory();
            int index = path.IndexOf(dto.setInfo.outputArea);
            path = path.Substring(0, index);

            string model_path = Path.Combine(path, dto.setInfo.modelOutputDir, table_name + ".cs");
            string controller_path = Path.Combine(path, dto.setInfo.controllerOutputDir, table_name + "Controller.cs");
            string interface_path = Path.Combine(path, dto.setInfo.interfaceOutputDir, "I" + table_name + "Service.cs");
            string implement_path = Path.Combine(path, dto.setInfo.implementOutputDir, table_name + "Service.cs");
            string html_path = Path.Combine(path, dto.setInfo.htmlOutputDir);

            if (!Directory.Exists(Path.GetDirectoryName(model_path))) Directory.CreateDirectory(Path.GetDirectoryName(model_path));
            if (!Directory.Exists(Path.GetDirectoryName(controller_path))) Directory.CreateDirectory(Path.GetDirectoryName(controller_path));
            if (!Directory.Exists(Path.GetDirectoryName(interface_path))) Directory.CreateDirectory(Path.GetDirectoryName(interface_path));
            if (!Directory.Exists(Path.GetDirectoryName(implement_path))) Directory.CreateDirectory(Path.GetDirectoryName(implement_path));
            if (!Directory.Exists(html_path)) Directory.CreateDirectory(html_path);

            File.WriteAllText(model_path, code.modelText);
            File.WriteAllText(controller_path, code.controllerText);
            File.WriteAllText(interface_path, code.interfaceText);
            File.WriteAllText(implement_path, code.implementText);
            File.WriteAllText(Path.Combine(html_path, table_name.ToLower() + ".html"), code.webText);
            File.WriteAllText(Path.Combine(html_path, table_name.ToLower() + "form.html"), code.formText);

            //await _moduleService.EditModuleAsync(dto.moduleInfo);

            return Success("ok");
        }
    }

    //类型转换
    public static class TypeConvertor
    {
        //将数据库类型映射成C#类型
        public static string MapType(string dbType)
        {
            if (string.IsNullOrEmpty(dbType))
            {
                return dbType;
            }

            dbType = dbType.ToLower();
            string csType = "object";
            switch (dbType)
            {
                case "char": csType = "string"; break;
                case "date": csType = "DateTime"; break;
                case "datetime": csType = "DateTime"; break;
                case "decimal": csType = "decimal"; break;
                case "float": csType = "double"; break;
                case "int": csType = "int"; break;
                case "money": csType = "decimal"; break;
                case "nchar": csType = "string"; break;
                case "ntext": csType = "string"; break;
                case "nvarchar": csType = "string"; break;
                case "smalldatetime": csType = "DateTime"; break;
                case "smallint": csType = "short"; break;
                case "smallmoney": csType = "decimal"; break;
                case "text": csType = "string"; break;
                case "time": csType = "TimeSpan"; break;
                case "varchar": csType = "string"; break;
                case "bit": csType = "bool"; break;
                default: csType = "object"; break;
            }
            return csType;
        }
    }
    /// <summary>
    /// 表结构信息
    /// </summary>
    [Serializable]
    public class SchemaInfo
    {
        /// <summary>
        /// 列描述信息
        /// </summary>
        public string columndesc { get; set; }

        /// <summary>
        /// 列数据类型
        /// </summary>
        public string columntype { get; set; }

        /// <summary>
        /// 列名称
        /// </summary>
        public string columnname { get; set; }
    }
}
